<template>
  <div id="spyglass-map" style="width: 100%; height: 100%;"></div>
</template>

<script setup lang="ts">
import { onMounted, onUnmounted, ref } from 'vue';
import Map from 'ol/Map';
import View from 'ol/View';
import TileLayer from 'ol/layer/Tile';
import { XYZ } from 'ol/source';
import { defaults as DefaultControls } from 'ol/control';
import { transform } from 'ol/proj';
import RenderEvent from 'ol/render/Event'; // 导入 RenderEvent 类型，用于 Canvas 渲染事件
import { unByKey } from 'ol/Observable'; // 导入 unByKey 用于移除事件监听器
import { EventsKey } from 'ol/events'; // 导入 EventsKey 类型，用于存储事件监听器的键
import { getRenderPixel } from 'ol/render'; // 用于在 prerender 事件中获取准确的 Canvas 像素

let map: Map | null = null;
let spyglassLayer: TileLayer | null = null; // 将 spyglassLayer 声明在 setup 作用域，以便在事件处理函数中访问和修改其可见性
let mousePosition = ref<number[] | null>(null); // 存储鼠标在地图容器内的像素坐标 (CSS像素)
let prerenderListenerKey: EventsKey | null = null; // 用于存储 prerender 事件监听器的键
let postrenderListenerKey: EventsKey | null = null; // 用于存储 postrender 事件监听器的键

onMounted(() => {
  // 定义底图图层，使用高德地图的普通地图服务
  const baseLayer = new TileLayer({
    source: new XYZ({
      // 高德地图底图URL，确保样式是业务图 (style=7)
      url: 'http://wprd0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}',
    }),
  });

  // 定义卷帘图层（即在圆形区域内显示的不同图层），使用高德地图的卫星图服务
  spyglassLayer = new TileLayer({ // 赋值给外部声明的变量
    source: new XYZ({
      url: 'https://webst0{1-4}.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}', // 高德卫星图URL
    }),
    visible: false, // 初始设置为不可见，只有鼠标进入时才显示
  });

  // 初始化 OpenLayers 地图
  map = new Map({
    controls: DefaultControls().extend([]), // 禁用所有默认控件，以提供更简洁的界面
    target: 'spyglass-map', // 地图渲染的目标HTML元素ID
    layers: [baseLayer, spyglassLayer], // 添加底图和卷帘图层
    view: new View({
      center: transform([114.30, 30.50], 'EPSG:4326', 'EPSG:3857'), // 地图初始中心点，从经纬度坐标系转换到Web墨卡托投影
      zoom: 12, // 初始缩放级别
      projection: 'EPSG:3857', // 使用Web墨卡托投影，与大多数在线瓦片地图服务兼容
    }),
  });

  // 监听地图的鼠标移动事件
  map.on('pointermove', (event) => {
    mousePosition.value = event.pixel; // 存储鼠标在地图容器内的像素坐标
    // 如果卫星图层当前不可见，则在鼠标进入地图时使其可见
    if (spyglassLayer && !spyglassLayer.getVisible()) {
      spyglassLayer.setVisible(true);
    }
    map?.render(); // 强制地图重新渲染，以便更新裁剪区域
  });

  // 监听鼠标移出地图视口事件
  map.getViewport().addEventListener('mouseout', () => {
    mousePosition.value = null; // 鼠标移出时清除位置信息
    // 鼠标移出时隐藏卫星图层
    if (spyglassLayer && spyglassLayer.getVisible()) {
      spyglassLayer.setVisible(false);
    }
    map?.render(); // 强制地图重新渲染，移除裁剪效果
  });

  // 为 spyglassLayer 添加 'prerender' 事件监听器
  // 这个事件在图层渲染之前触发，在这里设置Canvas的裁剪区域
  prerenderListenerKey = spyglassLayer.on('prerender', (event) => {
    // 确保事件上下文是 CanvasRenderingContext2D 类型
    const context = (event as RenderEvent).context as CanvasRenderingContext2D;
    // 只有当鼠标在地图上时才进行裁剪
    if (mousePosition.value) {
      context.save(); // 保存当前Canvas状态，以便后续恢复
      context.beginPath(); // 开始新的路径绘制

      // 使用 getRenderPixel 获取相对于当前 Canvas 上下文的准确像素坐标
      // 这是解决鼠标位置与圆圈中心不一致的关键
      const pixel = getRenderPixel(event, mousePosition.value);

      const radius = 75; // 裁剪圆的半径，可以根据需要调整大小
      // 绘制圆形路径，以像素位置为中心，指定半径
      context.arc(pixel[0], pixel[1], radius, 0, 2 * Math.PI);
      context.clip(); // 将圆形路径设置为裁剪区域，只有在此区域内的内容才会被渲染
    }
  });

  // 为 spyglassLayer 添加 'postrender' 事件监听器
  // 这个事件在图层渲染之后触发，在这里恢复Canvas状态，取消裁剪
  postrenderListenerKey = spyglassLayer.on('postrender', (event) => {
    // 确保事件上下文是 CanvasRenderingContext2D 类型
    const context = (event as RenderEvent).context as CanvasRenderingContext2D;
    // 只有当之前进行了裁剪（即 mousePosition.value 存在）才需要恢复状态
    if (mousePosition.value) {
      context.restore(); // 恢复之前保存的Canvas状态，取消裁剪，确保后续渲染不受影响
    }
  });

  console.log("SpyglassMap mounted.");
});

onUnmounted(() => {
  if (map) {
    map.setTarget(undefined); // 解除地图与DOM元素的绑定
    map = null; // 清空地图实例
  }
  // 移除事件监听器，避免内存泄漏
  if (prerenderListenerKey) {
    unByKey(prerenderListenerKey);
  }
  if (postrenderListenerKey) {
    unByKey(postrenderListenerKey);
  }
  spyglassLayer = null; // 清空图层实例
  console.log("SpyglassMap unmounted.");
});
</script>

<style scoped>
#spyglass-map {
  width: 100%;
  height: 100%;
}
</style> 